#pragma once

#include <iostream>
#include <cstring>
#include <cerrno>
#include <string>
#include <wait.h>
#include <signal.h>
#include <functional>

#include <sys/types.h>
#include <sys/socket.h>

#include <netinet/in.h>
#include <arpa/inet.h>


#include "Log.hpp"
#include "Common.hpp"
#include "InetAddr.hpp"
#include "ThreadPool.hpp"


uint16_t g_port = 8081;

#define BACKLOG 8
using namespace LogModule;
using namespace ThreadPoolModule;

class TcpServer
{
    using task_t = std::function<void()>;
    using handler_t = std::function<std::string(std::string&)>;
private:
    struct ThreadData{
        int _sockfd;
        TcpServer *_self;
    };
public:
    TcpServer(handler_t handler, int port = g_port)
        : _port(port), _isrunning(false)
        , _handler(handler)
    {
    }
    void InitServer()
    {
        // 1. 创建套接字
        _listensockfd = ::socket(AF_INET, SOCK_STREAM, 0); // sys/types.h sys/socket.h
        if (_listensockfd < 0)
        {
            LOG(LogLevel::FATAL) << "socket error";
            Die(SOCKET_ERR);
        }
        LOG(LogLevel::INFO) << "socket create success, sockfd is : " << _listensockfd;

        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);      // ?
        local.sin_addr.s_addr = INADDR_ANY; // 0

        // 2. 绑定
        int n = ::bind(_listensockfd, CONV(&local), sizeof(local));
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "bind error";
            Die(BIND_ERR);
        }
        LOG(LogLevel::INFO) << "bind success, sockfd is : " << _listensockfd;

        // 3. 设置socket为监听状态
        n = ::listen(_listensockfd, BACKLOG);
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "Listen error";
            Die(LISTEN_ERR);
        }
        LOG(LogLevel::INFO) << "listen success, sockfd is : " << _listensockfd;

        // 注册一个 signal 
        ::signal(SIGCHLD, SIG_IGN);
    }
    void HandlerRequest(int sockfd) // TCP 也是全双工通信的
    {
        char inbuffer[4096];
        std::string package;
        while(true)
        {
            // 约定： 用户发过来的是一个完整的命令string
            
            // 面向字节流 像文件 可以直接 read 流式处理
            // ssize_t n = ::read(sockfd, inbuffer, sizeof(inbuffer)-1);
            ssize_t n = ::recv(sockfd, inbuffer, sizeof(inbuffer) - 1, 0);
            // n 为实际读到的字节数
            if(n > 0)
            {
                inbuffer[n] = 0;
                LOG(LogLevel::INFO) << "\n" << inbuffer;
                package += inbuffer;
                std::string cmd_result = _handler(package);
                if(cmd_result.empty()) continue;
                ::send(sockfd, cmd_result.c_str(), cmd_result.size(), 0);
            }
            else if(n == 0)
            {
                LOG(LogLevel::INFO) << "client quit" << sockfd;
                break;
            }
            else {
                break;
            }
        }
        ::close(sockfd);
    }
    static void *ThreadEntry(void *args){
        ThreadData *data = (ThreadData*)args;
        data->_self->HandlerRequest(data->_sockfd);
        return nullptr;
    }
    void Start()
    {
        _isrunning = true;

        while (_isrunning)
        {
            // 不能直接读取数据
            // 1. 获取新链接
            struct sockaddr_in peer;
            socklen_t peerlen = sizeof(peer);
            //    服务对象                          output arguments
            int sockfd = ::accept(_listensockfd, CONV(&peer), &peerlen);
            if (sockfd < 0)
            {
                LOG(LogLevel::WARNING) << "accept error " << strerror(errno);
                continue;
            }
            // 获取连接成功
            LOG(LogLevel::INFO) << "accept success, sockfd is : " << sockfd;
            InetAddr addr(peer);
            LOG(LogLevel::INFO) << "Client info :" << addr.Addr();
            // 服务
            // version-1
            // HandlerRequest(sockfd);
            // version-2 : 多进程
            // pid_t id = fork();
            // if(id == 0){
            //     //
            //     // 关闭不需要的fd
            //     ::close(_listensockfd);
            //     if(fork() > 0){ exit(0); }
            //     // 孙子 进程
            //     HandlerRequest(sockfd);
            //     exit(0);
            // }
            // ::close(sockfd);
            // int rid = ::waitpid(id, nullptr, 0);
            // if(rid < 0)
            // {
            //     LOG(LogLevel::WARNING) << "waitpid error";
            // }
            /**
             * 这里不能直接使用我们的 进程池 我们之前写的需要先把子进程创建出来再分配（accept） sockfd（客户端套接字），这样就不能把 需要服务的 客户端套接字
             * 很好地传递给子进程，但是也不是没有路可以走， 我们可以使用进程间传递文件描述符的方式解决这一问题，将父进程accept的客户端套接字传给早已创建的子进程
             * 就可以使得子进程看到 客户端套接字 并执行客户端要求的服务
             */

            // version-3 : 多线程
            // pthread_t tid;
            // ThreadData *data = new ThreadData;
            // data->_sockfd = sockfd;
            // data->_self = this;
            // pthread_create(&tid, nullptr, ThreadEntry, data);

            // version-4 : 线程池
            // 适合短任务/用户量少 但是可以线程多弄一点 测试使用
            // task_t f = std::bind(&TcpServer::HandlerRequest, this,sockfd); // 构建任务 或者 Lambda
            ThreadPool<task_t>::getInstance()->Enqueue([this, sockfd](){
                this->HandlerRequest(sockfd);
            });
            
        }
    }
    void Stop() { _isrunning = false; }
    ~TcpServer()
    {
    }

private:
    int _listensockfd;
    uint16_t _port;
    bool _isrunning;

    // 处理上层任务的入口
    handler_t _handler;
};